/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package simplemetrosystem.view.canvas;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.Line2D;
import java.awt.geom.Rectangle2D;
import java.awt.geom.RoundRectangle2D;
import java.util.Observable;
import java.util.Observer;
import javax.swing.JPanel;
import simplemetrosystem.control.ElementManager;
import simplemetrosystem.model.Element.MetroEdge;
import simplemetrosystem.model.Element.MetroLine;
import simplemetrosystem.model.Element.MetroStation;
import simplemetrosystem.model.Element.MetroString;
import simplemetrosystem.view.MetroFrame;
import simplemetrosystem.view.canvas.MouseHandlers.BasicMouseHandler;
import simplemetrosystem.view.canvas.MouseHandlers.CalculHandler;
import simplemetrosystem.view.canvas.MouseHandlers.NewEdgeHandler;
import simplemetrosystem.view.canvas.MouseHandlers.NewStationHandler;
import simplemetrosystem.view.canvas.MouseHandlers.NewTextHandler;
import simplemetrosystem.view.canvas.MouseHandlers.RemoveHandler;
import simplemetrosystem.view.canvas.MouseHandlers.SelectedMouseHandler;
import simplemetrosystem.view.canvas.MouseHandlers.SelectingMouseHandler;

/**
 *
 * @author Eric Qian
 */
public class MetroCanvas extends JPanel implements Observer {

    /**
     * 
     * @param frame
     * @param i 
     */
    public MetroCanvas(MetroFrame frame, int i) {
        super();
        this.frame = frame;
        elementManager = ElementManager.getInstance();

        if (i < 0) {
            all = true;
        } else {
            this.line = elementManager.getLineAt(i);
            all = false;
        }

        canvasModel = CanvasModel.getInstance();
        canvasModel.addObserver(this);

        setBackground(Color.WHITE);

        theSize = new Dimension(canvasModel.getMatrixWidth() * canvasModel.getMatrixSize(),
                canvasModel.getMatrixHeight() * canvasModel.getMatrixSize());
        setPreferredSize(theSize);

        this.matrix = true;
        this.selected = false;
        this.newStation = false;
        this.newEdge = false;

        selectRec = new Rectangle2D.Double();
        nStation = new RoundRectangle2D.Double();
        nEdge = new Line2D.Double();

        this.setSelectingMouseHandler();
    }

    /**
     * 
     * @param matrixSize
     */
    public void setMatrixSize(int matrixSize) {
        canvasModel.setMatrixSize(matrixSize);
        theSize.height = canvasModel.getMatrixHeight() * canvasModel.getMatrixSize();
        theSize.width = canvasModel.getMatrixWidth() * canvasModel.getMatrixSize();
        setPreferredSize(getPreferredSize());
    }

    /**
     * 
     * @return
     */
    public int getMatrixSize() {
        return canvasModel.getMatrixSize();
    }

    @Override
    public Dimension getPreferredSize() {
        return theSize;
    }

    public void setMatrix(boolean matrix) {
        this.matrix = matrix;
        repaint();
    }
    
    public boolean getMatrix(){
        return matrix;
    }
    
    public void setSelected(boolean selected) {
        this.selected = selected;
    }

    public void setNewStation(boolean newStation) {
        this.newStation = newStation;
        repaint();
    }

    public void setNewEdge(boolean newEdge) {
        this.newEdge = newEdge;
        repaint();
    }

    public void setSelectRec(int x, int y, int w, int h) {
        selectRec.setRect(x, y, w, h);
    }

    public void setNewStationRec(int x, int y) {
        nStation.setRoundRect(x, y,
                canvasModel.getMatrixSize() * 4, canvasModel.getMatrixSize() * 2,
                canvasModel.getMatrixSize() / 2, canvasModel.getMatrixSize() / 2);
        repaint();
    }

    public void setNewEdgeLine(int x1, int y1, int x2, int y2) {
        nEdge.setLine(x1, y1, x2, y2);
        repaint();
    }

    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D gg = (Graphics2D) g;

        gg.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

        if (matrix) {
            drawMatrix(gg);
        }
        if (selected) {
            drawSelecting(gg);
        }
        if (newStation) {
            drawNewStation(gg);
        }
        if (newEdge) {
            drawNewEdge(gg);
        }

        for (int i = 0; i < elementManager.getEdgesSize(); i++) {
            MetroEdge aEdge = elementManager.getEdgeAt(i);
            aEdge.draw(gg, canvasModel.getMatrixSize());
            if (aEdge.isHighLight()) {
                aEdge.drawHighLight(gg, canvasModel.getMatrixSize());
            }
        }

        for (int i = 0; i < elementManager.getStationsSize(); i++) {
            MetroStation aStation = elementManager.getStationAt(i);
            aStation.draw(gg, canvasModel.getMatrixSize());
            if (aStation.isHighLight()) {
                aStation.drawHighLight(gg, canvasModel.getMatrixSize());
            }
            if (aStation.isSelected()) {
                aStation.drawSelected(gg, canvasModel.getMatrixSize());
            }
            if (aStation.isShadow()) {
                aStation.drawShadow(gg, canvasModel.getMatrixSize());
            }
        }

        for (int i = 0; i < elementManager.getStringSize(); i++) {
            MetroString aString = elementManager.getStringAt(i);
            aString.draw(gg, canvasModel.getMatrixSize());
            if (aString.isHighLight()) {
                aString.drawHighLight(gg, canvasModel.getMatrixSize());
            }
            if (aString.isSelected()) {
                aString.drawSelected(gg, canvasModel.getMatrixSize());
            }
            if (aString.isShadow()) {
                aString.drawShadow(gg, canvasModel.getMatrixSize());
            }
        }
    }

    private void drawMatrix(Graphics2D gg) {
        Graphics2D g2D = (Graphics2D) gg.create();

        g2D.setColor(Color.LIGHT_GRAY);

        int size = canvasModel.getMatrixSize() > 20 ? canvasModel.getMatrixSize() : 20;

        for (int i = 0; i < canvasModel.getMatrixWidth(); i++) {
            g2D.drawLine(i * size, 0, i * size, canvasModel.getMatrixHeight() * size);
        }

        for (int i = 0; i < canvasModel.getMatrixHeight(); i++) {
            g2D.drawLine(0, i * size, canvasModel.getMatrixWidth() * size, i * size);
        }
    }

    private void drawSelecting(Graphics2D gg) {
        Graphics2D g2D = (Graphics2D) gg.create();

        g2D.setColor(Color.BLACK);
        g2D.setStroke(new BasicStroke(1));

        g2D.draw(selectRec);
    }

    private void drawNewStation(Graphics2D gg) {
        Graphics2D g2D = (Graphics2D) gg.create();

        g2D.setColor(Color.DARK_GRAY);
        int strokeWidth = canvasModel.getMatrixSize() / 10 > 1
                ? canvasModel.getMatrixSize() / 10 : 2;
        g2D.setStroke(new BasicStroke(strokeWidth));
        g2D.draw(nStation);
    }

    private void drawNewEdge(Graphics2D gg) {
        Graphics2D g2D = (Graphics2D) gg.create();

        g2D.setColor(Color.DARK_GRAY);
        g2D.setStroke(new BasicStroke(canvasModel.getMatrixSize()));

        g2D.draw(nEdge);
    }

    public void update(Observable o, Object arg) {
        if (o instanceof CanvasModel) {
            repaint();
        }
    }

    /**
     * 
     */
    public void setSelectingMouseHandler() {
        if (selectingMouseHandler == null) {
            selectingMouseHandler = new SelectingMouseHandler(frame);
        }
        if (currentMouseHandler != null) {
            removeMouseListener(currentMouseHandler);
            removeMouseMotionListener(currentMouseHandler);
        }
        currentMouseHandler = selectingMouseHandler;
        this.addMouseListener(selectingMouseHandler);
        this.addMouseMotionListener(selectingMouseHandler);
    }

    /**
     * 
     */
    public void setSelectedMouseHandler() {
        if (selectedMouseHandler == null) {
            selectedMouseHandler = new SelectedMouseHandler(frame);
        }
        if (currentMouseHandler != null) {
            removeMouseListener(currentMouseHandler);
            removeMouseMotionListener(currentMouseHandler);
        }
        currentMouseHandler = selectedMouseHandler;
        this.addMouseListener(selectedMouseHandler);
        this.addMouseMotionListener(selectedMouseHandler);
    }

    public void setNewEdgeMouseHandler() {
        if (newEdgeHandler == null) {
            newEdgeHandler = new NewEdgeHandler(frame);
        }
        if (currentMouseHandler != null) {
            removeMouseListener(currentMouseHandler);
            removeMouseMotionListener(currentMouseHandler);
        }
        currentMouseHandler = newEdgeHandler;
        this.addMouseListener(newEdgeHandler);
        this.addMouseMotionListener(newEdgeHandler);

    }

    /**
     * 
     */
    public void setNewStationMouseHandler() {
        if (newStationHandler == null) {
            newStationHandler = new NewStationHandler(frame);
        }
        if (currentMouseHandler != null) {
            removeMouseListener(currentMouseHandler);
            removeMouseMotionListener(currentMouseHandler);
        }
        currentMouseHandler = newStationHandler;
        this.addMouseListener(newStationHandler);
        this.addMouseMotionListener(newStationHandler);
    }

    /**
     * 
     */
    public void setNewTextMouseHandler() {
        if (newTextHandler == null) {
            newTextHandler = new NewTextHandler(frame);
        }
        if (currentMouseHandler != null) {
            removeMouseListener(currentMouseHandler);
            removeMouseMotionListener(currentMouseHandler);
        }
        currentMouseHandler = newTextHandler;
        this.addMouseListener(newTextHandler);
        this.addMouseMotionListener(newTextHandler);
    }

    /**
     * 
     */
    public void setRemoveMouseHandler() {
        if (removeHandler == null) {
            removeHandler = new RemoveHandler(frame);
        }
        if (currentMouseHandler != null) {
            removeMouseListener(currentMouseHandler);
            removeMouseMotionListener(currentMouseHandler);
        }
        currentMouseHandler = removeHandler;
        this.addMouseListener(removeHandler);
        this.addMouseMotionListener(removeHandler);
    }

    /**
     * 
     */
    public void setCalculHandler() {
        if (calculHandler == null) {
            calculHandler = new CalculHandler(frame);
        }
        if (currentMouseHandler != null) {
            removeMouseListener(currentMouseHandler);
            removeMouseMotionListener(currentMouseHandler);
        }
        currentMouseHandler = calculHandler;
        this.addMouseListener(calculHandler);
        this.addMouseMotionListener(calculHandler);
    }
    private boolean all;
    private MetroFrame frame;
    private CanvasModel canvasModel;
    private ElementManager elementManager;
    private boolean selected;
    private boolean newStation;
    private boolean newEdge;
    private boolean matrix;
    private Rectangle2D selectRec;
    private RoundRectangle2D nStation;
    private Line2D nEdge;
    private MetroLine line;
    private Dimension theSize;
    private BasicMouseHandler currentMouseHandler;
    private SelectingMouseHandler selectingMouseHandler;
    private SelectedMouseHandler selectedMouseHandler;
    private NewEdgeHandler newEdgeHandler;
    private NewStationHandler newStationHandler;
    private NewTextHandler newTextHandler;
    private RemoveHandler removeHandler;
    private CalculHandler calculHandler;
}
